package com.echo.code.thread.mode.listener;

import static java.lang.Boolean.TRUE;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;

/**
 * Registry Created by baiyap on 2018/9/24.
 */
public class Registry {

  // 存储Subscriber集合和topic之间关系的map
  private ConcurrentHashMap<String, ConcurrentLinkedQueue<Subscriber>> subscriberContainer = new ConcurrentHashMap<>();

  public void bind(Object subscriber) {
    // 获取Subscriber Object的方法集合然后进行绑定
    List<Method> subscribeMethods = getSubscribeMethods(subscriber);
    subscribeMethods.forEach(m -> tieSubscriber(subscriber, m));
  }

  public void unbind(Object subscriber) {
    // unbind为了提高速度, 只对Subscriber进行失效操作
    subscriberContainer.forEach((key, queue) ->
        queue.forEach(s -> {
          if (s.getSubscribeObject() == subscriber) {
            s.setDisable(TRUE);
          }
        }));
  }

  private List<Method> getSubscribeMethods(Object subscriber) {
    List<Method> methods = new ArrayList<>();
    Class<?> clazz = subscriber.getClass();
    // 获取所有方法
    Method[] declaredMethods = clazz.getDeclaredMethods();
    // 方法为public &&且有一个入参 &&且被@Subscribe标记的方法才符合回调方法
    Arrays.stream(declaredMethods).filter(m -> m.isAnnotationPresent(Subscribe.class) && m.getParameterCount() == 1
        && m.getModifiers() == Modifier.PUBLIC).forEach(methods::add);
    return methods;
  }

  private void tieSubscriber(Object subscriber, Method method) {
    Subscribe subscribe = method.getDeclaredAnnotation(Subscribe.class);
    String topic = subscribe.value();
    // 当某topic没有Subscriber Queue的时候创建一个
    subscriberContainer.computeIfAbsent(topic, key -> new ConcurrentLinkedQueue<>());
    // 创建一个Subscriber并且加入Subscriber列表中
    subscriberContainer.get(topic).add(new Subscriber(subscriber, method));
  }

  public ConcurrentLinkedQueue<Subscriber> scanSubscriber(String topic) {
    return subscriberContainer.get(topic);
  }
}